package com.shisan.tingshu.album.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shisan.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.shisan.tingshu.album.mapper.AlbumInfoMapper;
import com.shisan.tingshu.album.mapper.AlbumStatMapper;
import com.shisan.tingshu.album.mapper.TrackInfoMapper;
import com.shisan.tingshu.album.service.AlbumAttributeValueService;
import com.shisan.tingshu.album.service.AlbumInfoService;
import com.shisan.tingshu.common.constant.SystemConstant;
import com.shisan.tingshu.common.execption.ShisanException;
import com.shisan.tingshu.common.rabbit.constant.MqConst;
import com.shisan.tingshu.common.rabbit.service.RabbitService;
import com.shisan.tingshu.common.util.AuthContextHolder;
import com.shisan.tingshu.model.album.AlbumAttributeValue;
import com.shisan.tingshu.model.album.AlbumInfo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shisan.tingshu.model.album.AlbumStat;
import com.shisan.tingshu.model.album.TrackInfo;
import com.shisan.tingshu.query.album.AlbumInfoQuery;
import com.shisan.tingshu.vo.album.AlbumAttributeValueVo;
import com.shisan.tingshu.vo.album.AlbumInfoVo;
import com.shisan.tingshu.vo.album.AlbumListVo;
import com.shisan.tingshu.vo.album.AlbumStatVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private AlbumAttributeValueService albumAttributeValueService;

	@Autowired
	private AlbumInfoService albumInfoService;

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private RabbitService rabbitService;

	/**
	 * 保存专辑信息
	 * @param albumInfoVo
	 */
	@Override
	// 对于 Spring 的事务来说，只有运行时异常和 Error 会触发事务回滚，RuntimeException 不会触发事务回滚
	// rollbackFor = Exception.class 表示当前方法抛出 Exception 异常时，会触发事务回滚
	@Transactional(rollbackFor = Exception.class)
	public void saveAlbumInfo(AlbumInfoVo albumInfoVo) {

		AlbumInfoServiceImpl proxyObjcet = (AlbumInfoServiceImpl) AopContext.currentProxy();

		// 入 tingshu_album 库，操作 album_info 表和 album_attribute_valye 表

		// 保存专辑基本信息到 album_info 表
		Long userId = AuthContextHolder.getUserId();
		AlbumInfo albumInfo = new AlbumInfo();
		BeanUtils.copyProperties(albumInfoVo, albumInfo);
		albumInfo.setUserId(userId);
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS); // 默认审核通过（TODO 对接后续系统）
		// 获取专辑的付费类型（0101免费、0101vip免费、0103付费）
		String payType = albumInfo.getPayType();
		if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)) {
			// 默认给这个专辑设置5集免费声音
			albumInfo.setTracksForFree(5);
		}
		int insert = albumInfoMapper.insert(albumInfo);
		log.info("保存专辑基本信息：{}", insert > 0 ? "成功" : "失败");

		// 保存专辑的标签信息到 album_attribute_value 表
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList(); // 获取选择的标签信息
		// 将 albumAttributeValueVoList 列表中的每个 AlbumAttributeValueVo 对象转换为 AlbumAttributeValue 对象，并将这些转换后的对象收集到一个新的 List<AlbumAttributeValue> 列表中
		// 通过 map 方法将每个 AlbumAttributeValueVo 对象转换为 AlbumAttributeValue 对象
		List<AlbumAttributeValue> attributeValues = albumAttributeValueVoList.stream().map(albumAttributeValueVo -> {
			// 创建一个新的 AlbumAttributeValue 对象
			AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
			//设置 AlbumAttributeValue 对象的 albumId 属性为 albumInfo.getId()
			albumAttributeValue.setAlbumId(albumInfo.getId());
			//设置 AlbumAttributeValue 对象的 attributeId 属性为 albumAttributeValueVo.getAttributeId()（专辑的属性 ID）
			albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
			//设置 AlbumAttributeValue 对象的 valueId 属性为 albumAttributeValueVo.getValueId()（属性值 ID）
			albumAttributeValue.setValueId(albumAttributeValueVo.getValueId());
			return albumAttributeValue;
		}).collect(Collectors.toList()); // 使用 collect(Collectors.toList()) 将转换后的 AlbumAttributeValue 对象收集到一个新的列表 attributeValues 中

		if(!CollectionUtils.isEmpty(attributeValues)){
			boolean b = albumAttributeValueService.saveBatch(attributeValues);
			log.info("保存专辑标签信息：{}", b ? "成功" : "失败");
		}

		// 保存专辑统计信息到 album_stat 表
		// 如果在一个事务方法中只是调用非事务方法，那么可以直接调用，比如：saveAlbumStat(albumInfo.getId());
		// 如果在一个事务方法中调用另一个事务方法，那么必须使用代理对象来调用
		// 注意：使用了 @Transactional 注解的方法必须是public的，否则事务不会生效
		// 代理对象调用有三种方式：下面以 saveAlbumStat() 为例
		// 1、将 saveAlbumStat() 抽取到一个接口中，然后调用，比如 tempService.saveAlbumStat()
		// 2、将自己视为一个代理对象，即先自己注入自己，然后调用，比如 albumInfoService.saveAlbumStat()
		// 注意：必须要开启循环依赖（Spring默认开启循环依赖，SpringBoot默认关闭）。即：srping.main.allow-circular-references: true # 允许循环依赖
		// 3、通过AOP上下文获取代理对象，然后调用
		// 注意：要在启动类上加上 @EnableAspectJAutoProxy(exposeProxy = true)
		proxyObjcet.saveAlbumStat(albumInfo.getId());

		// 将专辑同步到ES中（使用异步消息队列）
		String isOpen = albumInfoVo.getIsOpen();
		if ("1".equals(isOpen)) { // 如果专辑是公开的，就发送消息到专辑上架队列
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId().toString());
			log.info("专辑微服务发送消息到专辑上架队列成功");
		}
	}

	/**
	 * 分页展示用户创作的专辑列表
	 * @param pageParam
	 * @return
	 */
	@Override
	public IPage<AlbumListVo> findUserAlbumPage(IPage<AlbumListVo> pageParam, AlbumInfoQuery albumInfoQuery) {

		return albumInfoMapper.findUserAlbumPage(pageParam, albumInfoQuery);
	}

	/**
	 * 根据专辑id查询专辑数据
	 * @param albumId
	 * @return
	 */
	@Override
	public AlbumInfo getAlbumInfo(Long albumId) {
		// 根据专辑id查询专辑基本信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		if (albumInfo == null) {
			throw new ShisanException(201, "该专辑不存在");
		}

		// 查询专辑的标签信息（属性id和属性值id）
		List<AlbumAttributeValue> attributeValues = albumAttributeValueMapper
				.selectList(new LambdaQueryWrapper<AlbumAttributeValue>()
						.eq(AlbumAttributeValue::getAlbumId, albumId));
		albumInfo.setAlbumAttributeValueVoList(attributeValues);

		return albumInfo;
	}

	/**
	 * 修改专辑信息
	 * @param albumId
	 * @param albumInfoVo
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {

		// 方案一：先删除缓存，再更新数据库，在CdcEntityHandler中使用异步线程，再次删除缓存，同时加入了消息队列重试机制
//        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
//        redisTemplate.delete(cacheKey);

		// 修改专辑的基本信息
		// 先根据专辑id查询专辑的旧数据
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		if (albumInfo == null) {
			throw new ShisanException(201, "该专辑不存在");
		}
		BeanUtils.copyProperties(albumInfoVo, albumInfo); // 有的属性就直接拷贝过去，vo没有的属性用AlbumInfo中的旧数据

		if (SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfoVo.getPayType())) {
			albumInfo.setPrice(new BigDecimal("0.00"));
			albumInfo.setVipDiscount(new BigDecimal("-1.00"));
			albumInfo.setDiscount(new BigDecimal("-1.00"));
			albumInfo.setTracksForFree(0);
		} else {
			albumInfo.setTracksForFree(5);
		}

		int i = albumInfoMapper.updateById(albumInfo);
		log.info("修改专辑基本信息：{}", i > 0 ? "成功" : "失败");
		// 专辑的标签信息
		// 删除该专辑的旧标签数据
		LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(AlbumAttributeValue::getAlbumId, albumId);
		int delete = albumAttributeValueMapper.delete(wrapper);
		log.info("删除专辑的旧标签数据：{}", delete > 0 ? "成功" : "失败");

		// 查询该专辑的新标签信息
		List<AlbumAttributeValue> attributeValues = albumInfoVo.getAlbumAttributeValueVoList().stream().map(albumAttributeValueVo -> {
			AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
			albumAttributeValue.setAlbumId(albumId);
			albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
			albumAttributeValue.setValueId(albumAttributeValueVo.getValueId());
			return albumAttributeValue;
		}).collect(Collectors.toList());

		if (!CollectionUtils.isEmpty(attributeValues)) {
			boolean b = albumAttributeValueService.saveBatch(attributeValues);
			log.info("保存专辑标签信息：{}", b ? "成功" : "失败");
		}

		// 判断专辑是否公开，若公开则同步到ES中，然后会在首页展示
		String isOpen = albumInfoVo.getIsOpen();
		if("1".equals(isOpen)){
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId().toString());
			log.info("专辑微服务发送消息到专辑上架队列成功");
		}else{
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId().toString());
			log.info("专辑微服务发送消息到专辑下架队列成功");
		}
	}

	/**
	 * 根据专辑id删除专辑
	 * @param albumId
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void removeAlbumInfo(Long albumId) {
		// 删除专辑的基本信息
		// 查询专辑下是否有声音
		Long count = trackInfoMapper.selectCount(new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, albumId));
		if (count > 0) {
			throw new ShisanException(201, "该专辑下存在声音，请勿删除！");
		}
		int i = albumInfoMapper.deleteById(albumId);
		log.info("保存专辑标签信息：{}", i > 0 ? "成功" : "失败");
		// 删除专辑的标签信息
		int delete = albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
		log.info("删除专辑的标签信息：{}", delete > 0 ? "成功" : "失败");
		// 专辑的统计信息
		int delete1 = albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, albumId));
		log.info("删除专辑的统计信息：{}", delete1 > 0 ? "成功" : "失败");

		// 将专辑从ES中删除（使用异步消息队列）
		rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumId.toString());
		log.info("专辑微服务发送消息到专辑下架队列成功");
	}

	/**
	 * 查询当前用户创建过的专辑列表
	 * @param userId
	 * @return
	 */
	@Override
	public List<AlbumInfo> findUserAllAlbumList(Long userId) {
		return albumInfoMapper.selectList(new LambdaQueryWrapper<AlbumInfo>().eq(AlbumInfo::getUserId, userId));
	}

	/**
	 * 根据专辑id查询专辑统计信息
	 * @param albumId
	 * @return
	 */
	@Override
	public AlbumStatVo getAlbumStat(Long albumId) {
		return albumInfoMapper.getAlbumStat(albumId);
	}

	@Override
	public List<Long> getAlbumInfoIdList() {

		List<AlbumInfo> albumInfos = albumInfoMapper.selectList(null);
		List<Long> albumIds = albumInfos.stream().map(AlbumInfo::getId).collect(Collectors.toList());

		return albumIds;
	}

	/**
	 * 根据专辑id查询专辑信息
	 * @param trackId
	 * @return
	 */
	@Override
	public AlbumInfo getAlbumInfoByTrackId(Long trackId) {

		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		if (trackInfo == null) {
			throw new ShisanException(201, "该声音不存在");
		}
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
		if (albumInfo == null) {
			throw new ShisanException(201, "该声音对应的专辑不存在");
		}
		return albumInfo;
	}

	/**
	 * 查询所有的专辑id集合
	 * @return
	 */
	@Transactional
	public void saveAlbumStat(Long albumId) {
		ArrayList<String> albumStatus = new ArrayList<>();
		albumStatus.add(SystemConstant.ALBUM_STAT_PLAY);
		albumStatus.add(SystemConstant.ALBUM_STAT_SUBSCRIBE);
		albumStatus.add(SystemConstant.ALBUM_STAT_BROWSE);
		albumStatus.add(SystemConstant.ALBUM_STAT_COMMENT);
		for (String status : albumStatus) {
			AlbumStat albumStat = new AlbumStat();
			albumStat.setAlbumId(albumId);
			albumStat.setStatType(status);
			albumStat.setStatNum(0);
			albumStatMapper.insert(albumStat);
		}
	}
}